package cc.wanforme.st.server.interceptor;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;

/** referer 防盗链拦截器
 * @author wanne
 * @date 2022-10-20
 */
public class RefererInterceptor implements HandlerInterceptor {

	private List<ReferedPattern> patterns = new ArrayList<>();
	
	private boolean allowEmptyReferer;
	
	public RefererInterceptor(List<String> allowedPatterns, boolean allowEmptyReferer) {
		this.allowEmptyReferer = allowEmptyReferer;
		for (String patternValue : allowedPatterns) {
			ReferedPattern rp = new ReferedPattern(patternValue);
			patterns.add(rp);
		}
	}
	
	@Override
	public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler)
			throws Exception {
		if (patterns.isEmpty()) {
			return true;
		}

		String referer = req.getHeader("referer");
//	    String host = req.getServerName("x-forwarded-for");
		String serverHost = req.getServerName();
		if (referer == null) {
			// 状态置为404
			if(allowEmptyReferer) {
				return true;
			}
			resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
			return false;
		}
		java.net.URL url = null;
		try {
			url = new java.net.URL(referer);
		} catch (MalformedURLException e) {
			// URL解析异常，也置为404
			resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
			return false;
		}

		// 允许相同域名下的请求
		if (Objects.equals(url.getHost(), serverHost)) {
			return true;
		}

		String _referer = referer;
		int index = referer.indexOf('/', 8); // 'https://'
		if (index != -1) {
			_referer = referer.substring(0, index);
		}

		boolean allowed = this.checkAllowed(_referer);
		if (allowed) {
			return true;
		} else {
			resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
			return false;
		}

	}
	
	
	private boolean checkAllowed(String origin) {
		for (ReferedPattern pattern : patterns) {
			if(pattern.getPattern().matcher(origin).matches()) {
				return true;
			}
		}
		return false;
	}
	
}
